# coding=UTF-8
"""
定义模型
其中的模型
SearchableMixin
User
"""
from app import db,login
from datetime import datetime
from time import time
from flask import current_app
from flask_login import UserMixin
from werkzeug.security import generate_password_hash, check_password_hash
from hashlib import md5
import jwt
import json
from app.search import add_to_index, remove_from_index, query_index

followers = db.Table('followers',
	db.Column('follower_id', db.Integer, db.ForeignKey('user.id')),
	db.Column('followed_id', db.Integer, db.ForeignKey('user.id'))
)

class SearchableMixin(object):
	@classmethod
	def search(cls, expression, page, per_page):
		ids, total = query_index(cls.__tablename__, expression, page, per_page)
		if total == 0:
			return cls.query.filter_by(id = 0),0
		when = []
		for i in range(len(ids)):
			when.append((ids[i],i))
		return cls.query.filter(cls.id.in_(ids)).order_by(db.case(when, value = cls.id)),total
	
	@classmethod
	def before_commit(cls, session):
		session._changes = {
			'add': list(session.new),
			'update': list(session.dirty),
			'delete': list(session.deleted)
		}
	@classmethod
	def after_commit(cls, session):
		for obj in session._changes['add']:
			if isinstance(obj, SearchableMixin):
				add_to_index(obj.__tablename__, obj)
		for obj in session._changes['update']:
		  	if isinstance(obj, SearchableMixin):
				add_to_index(obj.__tablename__, obj)
		for obj in session._changes['delete']:
		  	if isinstance(obj, SearchableMixin):
				remove_from_index(obj.__tablename__, obj)
		session._changes = None
	@classmethod
	def reindex(cls):
		for obj in cls.query:
			add_to_index(cls.__tablename__, obj)
			
db.event.listen(db.session, 'before_commit', SearchableMixin.before_commit)
db.event.listen(db.session, 'after_commit', SearchableMixin.after_commit)


#用户类，继承UserMixin和db.Model
# ClassName: User
# ClassVariable:
# 	id ,username, email, possword_hash,posts,about_me, last_seen,followed
# ClassMethod:
#	avatar
# 	__repr__
# 	set_password
# 	check_password
class User(UserMixin,db.Model):

	id = db.Column(db.Integer,primary_key=True)
	# 用户名字
	username = db.Column(db.String(64),index = True,unique = True)
	# 用户邮箱
	email = db.Column(db.String(120),index = True,unique = True)
	# 用户密码的哈希值，安全考虑
	password_hash = db.Column(db.String(128))
	# 用户的帖子
	posts = db.relationship('Post',backref = 'author',lazy='dynamic')
	# 用户的自我介绍
	about_me = db.Column(db.String(140))
	# 用户最后登陆时间
	last_seen = db.Column(db.DateTime, default = datetime.utcnow)
	# 用户的关注者关系，使用关系表
	followed = db.relationship(
		'User',secondary = followers,
		primaryjoin = (followers.c.follower_id == id),
		secondaryjoin = (followers.c.followed_id == id),
		backref = db.backref('followers',lazy = 'dynamic'),lazy = 'dynamic')
	# 发送的消息
	message_sent = db.relationship(
		'Message',foreign_keys = 'Message.sender_id', backref='author',lazy = 'dynamic')
	
	# 接收消息
	messages_received = db.relationship(
		'Message', foreign_keys = 'Message.recipient_id', backref='recipient', lazy = 'dynamic')
	# 最后阅读消息的时间
	last_message_read_time = db.Column(db.DateTime)
	#通知消息
	notifications = db.relationship('Notification', backref='user', lazy = 'dynamic')
	# 返回新消息的个数 
	def new_messages(self):
		last_read_time = self.last_message_read_time or datetime(1900,1,1)
		return Message.query.filter_by(recipient=self).filter(Message.timestamp > last_read_time).count()
	def add_notification(self, name, data):
		self.notifications.filter_by(name = name).delete()
		n = Notification(name = name, payload_json = json.dumps(data), user=self)
		db.session.add(n)
		return n

	def avatar(self, size):
		digest = md5(self.email.lower().encode('utf-8')).hexdigest()
		return 'https://www.gravatar.com/avatar/{}?d=identicon&s={}'.format(digest,size)

	# print object of this class
	def __repr__(self):
		return '<User> {}'.format(self.username)

	def set_password(self, password):
		self.password_hash = generate_password_hash(password)

	def check_password(self, password):
		return check_password_hash(self.password_hash,password)
	
	# follow
	def follow(self, user):
		if not self.is_following(user):
			self.followed.append(user)
	
	# follow
	def unfollow(self, user):
		if self.is_following(user):
			self.followed.remove(user)
	
	# 检查是不是following
	def is_following(self, user):
		return self.followed.filter(followers.c.followed_id == user.id).count() > 0

	def followed_posts(self):
		followed = Post.query.join(followers,(followers.c.followed_id == Post.user_id)).filter(followers.c.follower_id == self.id)
		own = Post.query.filter_by(user_id = self.id)
		return followed.union(own).order_by(Post.timestamp.desc())
	
	def get_reset_password_token(self, expires_in=600):
		return jwt.encode({'reset_password' : self.id,'exp' : time()+expires_in},app.config['SECRET_KEY'],algorithm='HS256').decode('utf-8')
	
	@staticmethod
	def verify_reset_password_token(token):
		try:
			id = jwt.decode(token, app.config['SECERT_KEY'],algorithm=['HS256'])['reset_password']
		except:
			return
		return User.query.get(id)

@login.user_loader
def load_user(id):
	return User.query.get(int(id))

# represent blog posts written by user

class Post(SearchableMixin, db.Model):
	id = db.Column(db.Integer, primary_key = True)
	body = db.Column(db.String(140))
	timestamp = db.Column(db.DateTime, index=True, default = datetime.utcnow)
	user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
	# 一个属性变量
	__searchable__ = ['body']
	def __repr__(self):
		return '<Post {}>'.format(self.body)

# 定义一个消息模型
# Message
# id:Integer primary_key
# sender_id : Integer ForeignKey
# recipient_id : Integer ForeignKey
# body: String(140) 
# timestamp: DateTime 
class Message(db.Model):
	id = db.Column(db.Integer, primary_key = True)
	# 发送方id
	sender_id = db.Column(db.Integer, db.ForeignKey('user.id'))
	# 接收方id
	recipient_id = db.Column(db.Integer, db.ForeignKey('user.id'))
	# 消息内容设置
	body = db.Column(db.String(140))
	# 消息发送的时间
	timestamp = db.Column(db.DateTime, index=True,default = datetime.utcnow)
	
	def __repr__(self):
		return '<Message {}>',format(self.body)
	
"""
ClassName: Notification
ClassVriable:
	id:		Integer 	primary_key
	name:	String(128)	index
	user_id:	Integer		Foreign
	timestamp:	Float	index 	default = time
	payload_json:	Text
ClassMethods:
	get_data(self):
"""
class Notification(db.Model):
	id = db.Column(db.Integer, primary_key = True)
	name = db.Column(db.String(128),index = True)
	user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
	timestamp = db.Column(db.Float, index = True, default = time)
	payload_json = db.Column(db.Text)

	def get_data(self):
		return json.loads(str(self.payload_json))
